#!/usr/bin/env python3
import argparse
import os
import sys
import time
import grpc
import json
from datetime import datetime
import subprocess
# Import P4Runtime lib from parent utils dir
# Probably there's a better way of doing this.
sys.path.append(
    os.path.join(os.path.dirname(os.path.abspath(__file__)),
                 '../../utils/'))
import p4runtime_lib.bmv2
import p4runtime_lib.helper
from p4runtime_lib.error_utils import printGrpcError
from p4runtime_lib.switch import ShutdownAllSwitchConnections



def writeIpForwardRules(p4info_helper, ingress_sw, egress_sw, mymatch_fields, mydstaddr, myport):
    table_entry = p4info_helper.buildTableEntry(
        table_name="MyIngress.ipv4_lpm",
        match_fields={
            "hdr.ipv4.dstAddr": mymatch_fields
        },
        action_name="MyIngress.ipv4_forward",
        action_params={
            "dstAddr": mydstaddr,
            "port": myport
        })
    ingress_sw.WriteTableEntry(table_entry)

    print("Installed transit ipforward rule on %s" % ingress_sw.name)

def writeSwitchId(p4info_helper, ingress_sw, egress_sw,
                  swid):
    table_entry = p4info_helper.buildTableEntry(
        table_name="MyEgress.swtrace",
        default_action=True,
        action_name="MyEgress.add_swtrace",
        action_params={
            "swid": swid,
        }
    )
    ingress_sw.WriteTableEntry(table_entry)


def main(p4info_file_path, bmv2_file_path):
    p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_file_path)

    try:
        s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
            name='s1',
            address='127.0.0.1:50051',
            device_id=0,
            proto_dump_file='logs/s1-p4runtime-requests.txt')
        s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
            name='s2',
            address='127.0.0.1:50052',
            device_id=1,
            proto_dump_file='logs/s2-p4runtime-requests.txt')
        s3 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
            name='s3',
            address='127.0.0.1:50053',
            device_id=2,
            proto_dump_file='logs/s3-p4runtime-requests.txt')

        s1.MasterArbitrationUpdate()
        s2.MasterArbitrationUpdate()
        s3.MasterArbitrationUpdate()

        s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info,
                                       bmv2_json_file_path=bmv2_file_path)
        print("Installed P4 Program using SetForwardingPipelineConfig on s1")
        s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info,
                                       bmv2_json_file_path=bmv2_file_path)
        print("Installed P4 Program using SetForwardingPipelineConfig on s2")
        s3.SetForwardingPipelineConfig(p4info=p4info_helper.p4info,
                                       bmv2_json_file_path=bmv2_file_path)
        print("Installed P4 Program using SetForwardingPipelineConfig on s3")
        # s1 流规则下发
        writeIpForwardRules(p4info_helper, ingress_sw=s1, egress_sw=s1, mymatch_fields=["10.0.1.1", 32],
                            mydstaddr="08:00:00:00:01:01", myport=2)
        writeIpForwardRules(p4info_helper, ingress_sw=s1, egress_sw=s1, mymatch_fields=["10.0.1.11", 32],
                            mydstaddr="08:00:00:00:01:11", myport=1)
        writeIpForwardRules(p4info_helper, ingress_sw=s1, egress_sw=s2, mymatch_fields=["10.0.2.0", 24],
                            mydstaddr="08:00:00:00:02:00", myport=3)
        writeIpForwardRules(p4info_helper, ingress_sw=s1, egress_sw=s3, mymatch_fields=["10.0.3.0", 24],
                            mydstaddr="08:00:00:00:03:00", myport=4)




        # s2 流规则下发
        writeIpForwardRules(p4info_helper, ingress_sw=s2, egress_sw=s2, mymatch_fields=["10.0.2.2", 32],
                            mydstaddr="08:00:00:00:02:02", myport=2)
        writeIpForwardRules(p4info_helper, ingress_sw=s2, egress_sw=s2, mymatch_fields=["10.0.2.22", 32],
                            mydstaddr="08:00:00:00:02:22", myport=1)
        writeIpForwardRules(p4info_helper, ingress_sw=s2, egress_sw=s1, mymatch_fields=["10.0.1.0", 24],
                            mydstaddr="08:00:00:00:01:00", myport=3)
        writeIpForwardRules(p4info_helper, ingress_sw=s2, egress_sw=s3, mymatch_fields=["10.0.3.0", 24],
                            mydstaddr="08:00:00:00:03:00", myport=4)

        # s3 流规则下发
        writeIpForwardRules(p4info_helper, ingress_sw=s3, egress_sw=s3, mymatch_fields=["10.0.3.3", 32],
                            mydstaddr="08:00:00:00:03:03", myport=1)
        writeIpForwardRules(p4info_helper, ingress_sw=s3, egress_sw=s1, mymatch_fields=["10.0.1.0", 24],
                            mydstaddr="08:00:00:00:01:00", myport=2)
        writeIpForwardRules(p4info_helper, ingress_sw=s3, egress_sw=s2, mymatch_fields=["10.0.2.0", 24],
                            mydstaddr="08:00:00:00:02:00", myport=3)

        writeSwitchId(p4info_helper, s1, s1, swid=1)
        writeSwitchId(p4info_helper, s2, s2, swid=1)
        writeSwitchId(p4info_helper, s3, s3, swid=1)
        # get_mri0()
        #打印
        # while True:
        #     sleep(2)
        #     print('\n----- Reading tunnel counters -----')
        #     get_time_delay("h1","2",1,2)
        #     get_time_delay("h2","1",3,4)
        #     get_time_delay("h2","3",5,6)
        #     get_time_delay("h3","2",7,8)
        #     get_time_delay("h1","3",9,10)
        #     get_time_delay("h3","1",11,12)



    except KeyboardInterrupt:
        print(" Shutting down.")
    except grpc.RpcError as e:
        printGrpcError(e)

    ShutdownAllSwitchConnections()
def get_mri0():
    # 启动xterm窗口并在相应的Mininet主机上运行命令
    h2_process = run_xterm_command('h2', 'bash -c "sudo ./receive.py; exec bash"')
    h22_process = run_xterm_command('h22', 'iperf -s -u')
    h1_process = run_xterm_command('h1', 'bash -c "sudo ./send.py 10.0.1.1 10.0.2.2 \\"P4 is cool\\" 30; exec bash"')
    h11_process = run_xterm_command('h11', 'iperf -c 10.0.2.22 -t 15 -u')

    # 等待h2窗口的命令执行完成并获取输出
    output, errors = h2_process.communicate()

    # 将输出打印到外部终端
    print(output.decode())
    # 定义运行命令的函数
def run_xterm_command(host, command):
    return subprocess.Popen(['xterm', '-title', host, '-e', command])



def get_mri(addr):

    # 1. 打开四个XTerm窗口，并分别命名为 h1, h11, h2, h22
    # 使用 subprocess.Popen 来捕获输出
    # 2. 在窗口 h2 中运行捕获数据包的服务器命令 ./receive.py
    receive_process = subprocess.Popen(['xterm', '-hold','-title', 'h2', '-e', 'bash -c "sudo ./receive.py; exec bash"'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    # 3. 在窗口 h22 中运行 iperf UDP 服务器''
    print("h2start\n")
    time.sleep(2)
    subprocess.Popen(['xterm', '-hold','-title', 'h22', '-e', 'bash -c "iperf -s -u; exec bash"'])
    print("h22start\n")
    time.sleep(2)
    # subprocess.Popen(['xterm','-hold', '-title', 'h1', '-e', 'bash -c f"sudo ./send.py 10.0.{addr}.{addr} 10.0.2.2 \'P4 is cool\' 30; exec bash"'])
    cmd = f'xterm -hold -title h1 -e "sudo ./send.py 10.0.{addr}.{addr} 10.0.2.2 \'P4 is cool\' 100"'

    subprocess.Popen(['bash', '-c', cmd])
    print("h1start\n")
    # time.sleep(1)
    subprocess.Popen(['xterm', '-hold','-title', 'h11', '-e', 'bash -c "iperf -c 10.0.2.22 -t 99 -u; exec bash"'])
    print("h11start\n")
    # 等待一段时间，确保 receive.py 有足够的时间写入输出
    time.sleep(5)

    # 4. 读取窗口 h2 的命令运行结果并保存
    # 注意：这里的假设是 ./receive.py 会在运行结束后输出结果到 /tmp/h2_output.txt
    # with open('h2_output1.txt', 'r') as file:
    #     lines = file.readlines()
    #     for line in lines:
    #         print(line, end='')
            # 打印到外部终端
    # h2_process = os.popen('xterm -hold -title h2 -e "sudo ./receive.py > h2_output0.txt" &')

    # 3. 在窗口 h22 中运行 iperf UDP 服务器
    # os.popen('xterm -hold -title h22 -e "iperf -s -u" &')

    # 4. 在窗口 h1 中运行 ./send.py 命令
    # os.popen('xterm -hold -title h1 -e "sudo ./send.py 10.0.2.2 \'P4 is cool\' 30" &')

    # 5. 在窗口 h11 中运行 iperf 客户端发送命令
    # os.popen('xterm -hold -title h11 -e "iperf -c 10.0.2.22 -t 15 -u" &')
    # os.system('tail -f h2_output0.txt &')

def get_time_delay(hname, addr,id_one,id_two):
    try:
        # re = os.popen(f"mx {hname} ping -c 100 10.0.{addr}.{addr}").readlines()
        re = os.popen(f"sudo mn -c xterm {hname1} ping {hname2}").readlines()

    except Exception as e:
        print(f"Error running iperf command: {e}")
        return
    result = []
    m_index1 = str(id_one)
    m_index2 = str(id_two)
    print("原神啟動")

    for i in range(len(re)):  # 由于原始结果需要转换编码，所以循环转为utf8编码并且去除\n换行
        res = re[i].strip('\n')
        # print(str(res))
        result.append(res)
        # print(str(len(result)))

    if not result:
        print("No results from iperf command")
        return

    myresult = str(result[-1])

    print(myresult)
    print("这是一行")
    print(str(result[-1]))
    print(str(result[-2]))
    jsonresult = str(result[-2])
    jsonresultlist = jsonresult.split()
    jsonresult0 = str(result[-1])
    jsonresultlist0 = jsonresult0.split()
    js0 = str(jsonresultlist0[3]).split('/')
    with open("/home/p4/share_dir/test.json", 'rb') as f:# 使用只读模型，并定义名称为f
        params = json.load(f)  # 加载json文件
        params[m_index1]["RTT_avg"] = js0[1]
        params[m_index1]["packet_lostrate"] = jsonresultlist[6]
        params[m_index2]["RTT_avg"] = js0[1]
        params[m_index2]["packet_lostrate"] = jsonresultlist[6]  # code字段对应的值修改为404
    with open("/home/p4/share_dir/test.json", 'w') as r:
        # 将dict写入名称为r的文件中
        json.dump(params, r)
    # if len(jsonresultlist) < 8:
    #     print("Unexpected iperf output format")
    #     return

    print(jsonresultlist)
    print(jsonresultlist0)
    print(js0)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='P4Runtime Controller')
    parser.add_argument('--p4info', help='p4info proto in text format from p4c',
                        type=str, action="store", required=False,
                        default='./build/mri.p4.p4info.txt')
    parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c',
                        type=str, action="store", required=False,
                        default='./build/mri.json')
    args = parser.parse_args()

    if not os.path.exists(args.p4info):
        parser.print_help()
        print("\np4info file not found: %s\nHave you run 'make'?" % args.p4info)
        parser.exit(1)
    if not os.path.exists(args.bmv2_json):
        parser.print_help()
        print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json)
        parser.exit(1)
    main(args.p4info, args.bmv2_json)
